<?php

namespace App\Events;

use App\Models\User;
use GatewayWorker\Lib\Gateway;
use Illuminate\Container\Container;
use Illuminate\Support\Facades\Log;

class Workerman
{
    public static function onWorkerStart($businessWorker)
    {
        echo 'BusinessWorker Start', PHP_EOL;
    }

    public static function onWebSocketConnect($client_id, $data)
    {
        if (self::pass_client_id()) {
            echo 'WebSocket Connect:', $client_id, PHP_EOL;
        }
    }

    // 连接的时候
    public static function onConnect($client_id)
    {
        // 组合数据 方便前端解析
        $data = ['cmd' => 'connect', 'code' => 200, 'show' => "你的客户端id是：$client_id"];
        Gateway::sendToCurrentClient(json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 有消息时
     * @param int $client_id
     * @param mixed $message
     */
    public static function onMessage($client_id, $message)
    {
        // 转换编码
        $encode  = mb_detect_encoding($message, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"), true);
        $message = mb_convert_encoding($message, "UTF-8", $encode); //编码转换为utf-8
        //$encode  = mb_detect_encoding($message, array("ASCII", "UTF-8", "GB2312", "GBK", "BIG5"), true);

        // 显示接收的数据
        $dealObj  = new self();
        $showInfo = $dealObj->serverInfo($client_id, true);
        $showStr  = '===>>>【'.date('Y-m-d H:i:s').'】接收客户端消息：'.$showInfo;
        $showStr  .= '   接收到的数据：'.$message.chr(10);
        echo $showStr;
        //echo '编码'.$encode.chr(10);

        // TODO 可以保存到日志文件中
        Log::channel('myWorker')->info($message);

        // 客户端传递的是json数据
        // 如果没有传递数据 则不执行
        $_data   = json_decode($message, true);
        $command = isset($_data['cmd']) ? trim($_data['cmd']) : '';
        $command = strtolower($command);

        // 根据类型执行不同的业务
        try{
            // 判断
            if(empty($_data)){
                $msg = '请传递操作数据！！';
                $dealObj->sendErrorToCurrent($command, $msg);
                throw new \Exception($msg);
            }
            if(empty($command)){
                $msg = '请传递操作命令数据！！';
                $dealObj->sendErrorToCurrent($command, $msg);
                throw new \Exception($msg);
            }
            // 重新赋值 或许有时候有用
            $_data['client_id'] = $client_id;

            // 命令处理
            $commands   = explode('@', $command);
            $controller = str_replace('_', ' ', $commands[0]);
            $controller = str_replace(' ', '', ucwords($controller));

            // 操作命令的控制器
            //$dealer = 'App\Events\Chat\\'.ucwords($controller).'Command';
            $dealer = 'App\Http\Controllers\Instruct\\'.ucwords($controller).'Controller';
            $action = isset($commands[1]) ? $commands[1] : '';

            // 如果控制器不存在
            if(!class_exists($dealer)){
                $show = $dealer.'文件不存在';
                $msg  = '操作命令不正确！';
                $dealObj->sendErrorToCurrent($command, $msg, $show);
                throw new \Exception($show);
            }

            // 操作控制器
            $controller = Container::getInstance()->make($dealer);
            if(!method_exists($controller, $action)){
                $show = '操作方法不存在';
                $msg  = '操作命令不正确！';
                $dealObj->sendErrorToCurrent($command, $msg, $show);
                throw new \Exception($show);
            }

            // 初始化操作
            // 当客户端和传递的数据在操作类里面都可以使用
            $controller->init($client_id, $_data);

            // 判断用户是否是登录用户
            // 有些操作不需要判断用户是否登录的 如：第一步绑定用户
            if((!in_array($command, ['user@bang',])) && (empty($_data['no_check']))){
                if(!empty($_data['uid'])){
                    $my_id  = session('my_id');
                    $myList = Gateway::getClientIdByUid($_data['uid']);

                    // 通过session判断用户是不是同一个人
                    // TODO 同一个IP绑定多个用户，在发消息，可能会报错，这里判断是否是IP或者端口发生了改变
                    // 经测试：如果是相同的IP（同一台电脑测试）链接不同的用户，session是公用的，只是端口不一样
                    // 所以这里不能使用session作为判断
                    /*if($my_id != $_data['uid']){
                        $show = '请传递你自己的用户id！';
                        $show = '请传递你自己的用户id！client:'.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT'].' session:'.$my_id.';uid:'.$_data['uid'];
                        $dealObj->sendErrorToCurrent($command, $show);
                        throw new \Exception($show);
                    }*/

                    // 直接绑定的判断
                    if(!in_array($client_id, $myList)){
                        $show = '请传递你自己的用户id！';
                        //$show = '请传递你自己的用户id！client:'.$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT'].' session:'.$my_id.';uid:'.$_data['uid'];
                        $dealObj->sendErrorToCurrent($command, $show);
                        throw new \Exception($show);
                    }
                }
            }


            // 执行操作方法
            $result = $controller->$action($client_id, $_data);

            // 直接返回错误
            // 一般情况就没有返回 所以是null
            if($result === false){
                $msg = $controller->getError();
                $msg = is_array($msg) ? json_encode($msg, JSON_UNESCAPED_UNICODE) : $msg;
                throw new \Exception($msg);
            }

        } catch(\Exception $ex){
            $cline = chr(10);
            $error = "  无效操作命令：{$command}".$cline;
            $error .= "  客户端id：".$client_id.$cline;
            $error .= "  错误消息：".$ex->getMessage().$cline;
            $error .= '  '.$ex->getFile().':'.$ex->getLine().$cline.$cline;

            /*//数组键值 file line function class type args
            $errors = $ex->getTrace();
            foreach($errors as $_err){
                $error .= '  '.$_err['file'].':'.$_err['line'].$cline.$cline;
            }*/
            //var_dump(array_keys($errors[1]));
            echo "\033[31;49;1;4m ".$error."\033[0m";

            // TODO 可以保存到日志文件中
            Log::channel('myWorker')->info($error);
        }

    }

    /**
     * 当客户端断开连接时
     * @param integer $client_id 客户端id
     */
    public static function onClose($client_id)
    {
        // 退出消息
        $cline  = chr(10);
        $tuichu = "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id onClose:'退出'".$cline;
        echo "\033[32;49;1;5m ".$tuichu."\033[0m";
        //echo "\033[32;49;1;5m 端口：".$client_id."\033[0m";


        // 保存退出时间
        $client_type = session('client_type');
        $my_id       = session('my_id');
        $time        = time();
        $dealer      = 'App\Http\Controllers\Instruct\UserController';
        $controller  = Container::getInstance()->make($dealer);
        $action      = 'livetime';
        $result      = $controller->$action($client_id, ['uid' => $my_id]);  // 执行操作方法


        // 直接返回错误
        if(($result === false) || is_null($result)){
            $msg    = $controller->getError();
            $msg    = is_array($msg) ? json_encode($msg, JSON_UNESCAPED_UNICODE) : $msg;
            $tuichu = "退出失败:{$msg}".$cline.$cline;
            echo "\033[32;49;1;5m ".$tuichu."\033[0m";
        } else{
            $tuichu = "用户id:{$my_id} 客户端类型:{$client_type} 退出时间:$time onClose:'退出成功！！'".$cline.$cline;
            echo "\033[32;49;1;5m ".$tuichu."\033[0m";
        }

        // 从房间的客户端列表中删除
        /*if(isset($_SESSION['room_id'])){
            $room_id     = $_SESSION['room_id'];
            $new_message = array('type' => 'logout', 'from_client_id' => $client_id, 'from_client_name' => $_SESSION['client_name'], 'time' => date('Y-m-d H:i:s'));
            Gateway::sendToGroup($room_id, json_encode($new_message, JSON_UNESCAPED_UNICODE));
        }*/
        /*try{
        } catch(\Exception $ex){
            $cline = chr(10);
            $error = "  客户端id：".$client_id.$cline;
            $error .= "  错误消息：".$ex->getMessage().$cline;
            $error .= '  '.$ex->getFile().':'.$ex->getLine().$cline.$cline;

            echo "\033[31;49;1;4m ".$error."\033[0m";

            // TODO 可以保存到日志文件中
            Log::channel('myWorker')->info($error);
        }*/
    }

    #################################################################################################
    ## 被调用的
    // 给自己返回错误
    private function sendErrorToCurrent($command, $message, $show = '')
    {
        $data = ['cmd' => $command, 'code' => 400, 'show' => $show, 'message' => $message];
        Gateway::sendToCurrentClient(json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    // 获取服务器与客户端的信息
    private function serverInfo($client_id, $is_string = true)
    {
        //  //echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n";
        $info = [
            'client'    => [
                'ip'   => $_SERVER['REMOTE_ADDR'],
                'port' => $_SERVER['REMOTE_PORT'],
            ],
            'gateway'   => [
                'ip'   => $_SERVER['GATEWAY_ADDR'],
                'port' => $_SERVER['GATEWAY_PORT'],
            ],
            'client_id' => $client_id, // $_SERVER['GATEWAY_CLIENT_ID']
        ];

        if($is_string){
            $cline  = chr(10);
            $string = '客户端：ip'.$info['client']['ip'].'，客户端端口：'.$info['client']['port'];
            $string .= '客户端id：'.$info['client_id'];
            $string .= '，服务端：ip'.$info['gateway']['ip'].'，客户端端口：'.$info['gateway']['port'].$cline;

            return $string;
        }

        return $info;
    }

    #################################################################################################
    // 未使用的
    // 消息处理
    public static function _onMessage($client_id, $message)
    {
        // 显示接收的数据
        echo (new self())->serverInfo($client_id, true);

        // 客户端传递的是json数据
        $message_data = json_decode($message, true);
        if(!$message_data){
            return;
        }

        // 根据类型执行不同的业务
        switch($message_data['type']){
            // 客户端回应服务端的心跳
            case 'pong':
                return;
            // 客户端登录 message格式: {type:login, name:xx, room_id:1} ，添加到客户端，广播给所有客户端xx进入聊天室
            case 'login':
                // 判断是否有房间号
                if(!isset($message_data['room_id'])){
                    throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message");
                }

                // 把房间号昵称放到session中
                $room_id                 = $message_data['room_id'];
                $client_name             = htmlspecialchars($message_data['client_name']);
                $_SESSION['room_id']     = $room_id;
                $_SESSION['client_name'] = $client_name;

                // 获取房间内所有用户列表
                $clients_list = Gateway::getClientSessionsByGroup($room_id);
                foreach($clients_list as $tmp_client_id => $item){
                    $clients_list[$tmp_client_id] = $item['client_name'];
                }
                $clients_list[$client_id] = $client_name;

                // 转播给当前房间的所有客户端，xx进入聊天室 message {type:login, client_id:xx, name:xx}
                $new_message = array('type' => $message_data['type'], 'client_id' => $client_id, 'client_name' => htmlspecialchars($client_name), 'time' => date('Y-m-d H:i:s'));
                Gateway::sendToGroup($room_id, json_encode($new_message));
                Gateway::joinGroup($client_id, $room_id);

                // 给当前用户发送用户列表
                $new_message['client_list'] = $clients_list;
                Gateway::sendToCurrentClient(json_encode($new_message));

                return;

            // 客户端发言 message: {type:say, to_client_id:xx, content:xx}
            case 'say':
                // 非法请求
                if(!isset($_SESSION['room_id'])){
                    throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
                }
                $room_id     = $_SESSION['room_id'];
                $client_name = $_SESSION['client_name'];

                // 私聊
                if($message_data['to_client_id'] != 'all'){
                    $new_message = array(
                        'type'             => 'say',
                        'from_client_id'   => $client_id,
                        'from_client_name' => $client_name,
                        'to_client_id'     => $message_data['to_client_id'],
                        'content'          => "<b>对你说: </b>".nl2br(htmlspecialchars($message_data['content'])),
                        'time'             => date('Y-m-d H:i:s'),
                    );
                    Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));
                    $new_message['content'] = "<b>你对".htmlspecialchars($message_data['to_client_name'])."说: </b>".nl2br(htmlspecialchars($message_data['content']));

                    return Gateway::sendToCurrentClient(json_encode($new_message));
                }

                $new_message = array(
                    'type'             => 'say',
                    'from_client_id'   => $client_id,
                    'from_client_name' => $client_name,
                    'to_client_id'     => 'all',
                    'content'          => nl2br(htmlspecialchars($message_data['content'])),
                    'time'             => date('Y-m-d H:i:s'),
                );

                return Gateway::sendToGroup($room_id, json_encode($new_message));
        }
    }
}




